﻿using System;
using System.Windows;
using Microsoft.Phone.BackgroundAudio;

namespace WindowsPhoneAudioStreaming.Audio.Streaming.Agent
{
    public class AudioPlayer : AudioPlayerAgent
    {
        private static volatile bool classInitialized;
        static IAudioPlayerService currentPlayingService;
        static int currentTrackNumber;

        /// <remarks>
        /// AudioPlayer instances can share the same process. 
        /// Static fields can be used to share state between AudioPlayer instances
        /// or to communicate with the Audio Streaming agent.
        /// </remarks>
        public AudioPlayer()
        {
            if (!classInitialized)
            {
                classInitialized = true;

                // Subscribe to the managed exception handler
                Deployment.Current.Dispatcher.BeginInvoke(() => Application.Current.UnhandledException += AudioPlayer_UnhandledException);

                currentPlayingService = InitializePlayingService();
            }
        }

        protected virtual IAudioPlayerService InitializePlayingService()
        {
            return null;
        }

        /// Code to execute on Unhandled Exceptions
        private void AudioPlayer_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            if (System.Diagnostics.Debugger.IsAttached)
            {
                // An unhandled exception has occurred; break into the debugger
                System.Diagnostics.Debugger.Break();
            }
        }

        protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState)
        {
            switch (playState)
            {
                case PlayState.TrackReady:
                    player.Volume = 1; //Volume level 0-1. Default .85
                    player.Play();
                    break;

                case PlayState.TrackEnded:
                    NextTrack(player);
                    break;

                case PlayState.Shutdown:
                    break;

                case PlayState.Unknown:
                    break;

                case PlayState.Stopped:
                    break;

                case PlayState.Paused:
                    break;

                case PlayState.Playing:
                    break;

                case PlayState.BufferingStarted:
                    break;

                case PlayState.BufferingStopped:
                    break;

                case PlayState.Rewinding:
                    break;

                case PlayState.FastForwarding:
                    break;
            }

            NotifyComplete();
        }


        /// <summary>
        /// Called when the user requests an action using application/system provided UI
        /// </summary>
        /// <param name="player">The BackgroundAudioPlayer</param>
        /// <param name="track">The track playing at the time of the user action</param>
        /// <param name="action">The action the user has requested</param>
        /// <param name="param">The data associated with the requested action.
        /// In the current version this parameter is only for use with the Seek action,
        /// to indicate the requested position of an audio track</param>
        /// <remarks>
        /// User actions do not automatically make any changes in system state; the agent is responsible
        /// for carrying out the user actions if they are supported.
        /// 
        /// Call NotifyComplete() only once, after the agent request has been completed, including async callbacks.
        /// </remarks>
        protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
        {
            switch (action)
            {
                case UserAction.Play:

                    if (PlayState.Playing != player.PlayerState)
                    {
                        if (player.Track == null)
                        {
                            currentTrackNumber = 0;

                            player.Track = currentPlayingService.TrackByIndex(currentTrackNumber);
                            //if (currentPlayingService.ActiveTrackList != null && currentPlayingService.ActiveTrackList.Length > 0)
                            //{
                            //    player.Track = currentPlayingService.ActiveTrackList[currentTrackNumber].ToAudioTrack();
                            //}
                        }
                        else
                        {
                            // Reset the stream lock to prevent reading
                            // from the http request for streaming. This
                            // is ignored for non-streaming playback
                            BufferedMediaStreamSource.StreamLock.Set();

                            player.Play();
                        }
                    }
                    break;

                case UserAction.Pause:
                case UserAction.Stop:
                    if (PlayState.Playing == player.PlayerState)
                    {
                        // Reset the stream lock to prevent reading
                        // from the http request for streaming. This
                        // is ignored for non-streaming playback
                        BufferedMediaStreamSource.StreamLock.Reset();
                        player.Stop();

                    }
                    break;


                case UserAction.FastForward:
                    // Fast Forward only works with non-MSS clients.
                    // If the Source is null, we are streaming an MSS.
                    if (track.Source != null)
                    {
                        player.FastForward();
                    }
                    break;

                case UserAction.Rewind:
                    // Rewind only works with non-MSS clients.
                    // If the Source is null, we are streaming an MSS.
                    if (track.Source != null)
                    {
                        player.Rewind();
                    }
                    break;

                case UserAction.Seek:
                    // Seek only works with non-MSS clients.
                    // If the Source is null, we are streaming an MSS.
                    if (track.Source != null)
                    {
                        player.Position = (TimeSpan)param;
                    }
                    break;

                case UserAction.SkipNext:

                    NextTrack(player);

                    //if (currentTrackNumber < currentPlayingService.ActiveTrackList.Length - 1)
                    //{
                    //    currentTrackNumber += 1;
                    //    player.Track = currentPlayingService.ActiveTrackList[currentTrackNumber].ToAudioTrack();
                    //}
                    //else
                    //{
                    //    player.Track = null;
                    //}
                    break;

                case UserAction.SkipPrevious:

                    PreviousTrack(player);
                    //if (currentTrackNumber > 0)
                    //{
                    //    currentTrackNumber -= 1;
                    //    player.Track = currentPlayingService.ActiveTrackList[currentTrackNumber].ToAudioTrack();
                    //}
                    //else
                    //{
                    //    player.Track = null;
                    //}
                    break;
            }

            NotifyComplete();
        }

        private void PreviousTrack(BackgroundAudioPlayer player)
        {
            // Determine the index of the next track
            currentTrackNumber = currentPlayingService.PreviousTrackIndex(currentTrackNumber);

            LoadTrack(player);
        }

        private void LoadTrack(BackgroundAudioPlayer player)
        {
            // Retrieve the track itself
            player.Track = currentTrackNumber >= 0 ? currentPlayingService.TrackByIndex(currentTrackNumber) : null;
        }

        private void NextTrack(BackgroundAudioPlayer player)
        {
            // Determine the index of the next track
            currentTrackNumber = currentPlayingService.NextTrackIndex(currentTrackNumber);

            LoadTrack(player);
        }


        /// <summary>
        /// Called whenever there is an error with playback, such as an AudioTrack not downloading correctly
        /// </summary>
        /// <param name="player">The BackgroundAudioPlayer</param>
        /// <param name="track">The track that had the error</param>
        /// <param name="error">The error that occured</param>
        /// <param name="isFatal">If true, playback cannot continue and playback of the track will stop</param>
        /// <remarks>
        /// This method is not guaranteed to be called in all cases. For example, if the background agent 
        /// itself has an unhandled exception, it won't get called back to handle its own errors.
        /// </remarks>
        protected override void OnError(BackgroundAudioPlayer player, AudioTrack track, Exception error, bool isFatal)
        {
            if (isFatal)
            {
                Abort();
            }
            else
            {
                NotifyComplete();
            }

        }

        /// <summary>
        /// Called when the agent request is getting cancelled
        /// </summary>
        /// <remarks>
        /// Once the request is Cancelled, the agent gets 5 seconds to finish its work,
        /// by calling NotifyComplete()/Abort().
        /// </remarks>
        protected override void OnCancel()
        {

        }
    }
}
